Generator Class

package javaxt.utils;
import java.util.Iterator;
import java.util.NoSuchElementException;

//******************************************************************************
//**  Generator
//******************************************************************************
/**
 *   A custom iterator that yields its values one at a time. This is a great
 *   alternative to arrays or lists when dealing with large data. By yielding
 *   one entry at a time, this iterator can help avoid out of memory exceptions.
 *
 *   Subclasses must define a method called {@link #run()} and may call
 *   {@link yield(T)} to return values one at a time. Example:
 <pre>
    Generator<String> generator = new Generator<String>() {

        @Override
        public void run() {

            BufferedReader br = file.getBufferedReader("UTF-8");

            String row;
            while ((row = br.readLine()) != null){
                yield(row);
            }

            br.close();
        }
    };
 </pre>
 *
 *   Clients can iterate through the generated results using standard iterators
 *   or an enhanced for loop like this:
 <pre>
    for (String row : generator){
        System.out.println(row);
    }
 </pre>
 *
 *   @author Michael Herrmann
 *   https://github.com/mherrmann/java-generator-functions
 *
 ******************************************************************************/

public abstract class Generator<T> implements Iterable<T>, AutoCloseable {

    private class Condition {
        private boolean isSet;
        public synchronized void set() {
            isSet = true;
            notify();
        }
        public synchronized void await() throws InterruptedException {
            try {
                if (isSet)
                        return;
                wait();
            } finally {
                isSet = false;
            }
        }
    }

    static ThreadGroup THREAD_GROUP;

    Thread producer;
    private boolean hasFinished;
    private final Condition itemAvailableOrHasFinished = new Condition();
    private final Condition itemRequested = new Condition();
    private T nextItem;
    private boolean nextItemAvailable;
    private RuntimeException exceptionRaisedByProducer;

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                    return waitForNext();
            }
            @Override
            public T next() {
                if (!waitForNext())
                        throw new NoSuchElementException();
                nextItemAvailable = false;
                return nextItem;
            }
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
            private boolean waitForNext() {
                if (nextItemAvailable)
                        return true;
                if (hasFinished)
                        return false;
                if (producer == null)
                        startProducer();
                itemRequested.set();
                try {
                        itemAvailableOrHasFinished.await();
                } catch (InterruptedException e) {
                        hasFinished = true;
                }
                if (exceptionRaisedByProducer != null)
                        throw exceptionRaisedByProducer;
                return !hasFinished;
            }
        };
    }

    public void close(){}

    protected abstract void run() throws InterruptedException;

    protected void yield(T element) throws InterruptedException {
        nextItem = element;
        nextItemAvailable = true;
        itemAvailableOrHasFinished.set();
        itemRequested.await();
    }

    private void startProducer() {
        assert producer == null;
        if (THREAD_GROUP == null)
                THREAD_GROUP = new ThreadGroup("generatorfunctions");
        producer = new Thread(THREAD_GROUP, new Runnable() {
                @Override
                public void run() {
                        try {
                                itemRequested.await();
                                Generator.this.run();
                        } catch (InterruptedException e) {
                                // No need to do anything here; Remaining steps in run()
                                // will cleanly shut down the thread.
                        } catch (RuntimeException e) {
                                exceptionRaisedByProducer = e;
                        }
                        hasFinished = true;
                        itemAvailableOrHasFinished.set();
                }
        });
        producer.setDaemon(true);
        producer.start();
    }

    @Override
    protected void finalize() throws Throwable {
        producer.interrupt();
        producer.join();
        super.finalize();
    }
}